/**
 * @author Vitor Franco do Carmo (vitor.carmo@cpmbraxis.com)
 * @since 03/08/2013
 *
 */
package br.com.vfcfw.test.functional;

import java.util.List;

import junit.framework.TestCase;

import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.ElementNotVisibleException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;

/**
 * Classe com os comandos do Selenium usados nos testes funcionais.
 * 
 * @author Vitor Franco do Carmo (vitor.carmo@cpmbraxis.com)
 * @since 03/08/2013
 * 
 */
public class ComandosSelenium extends TestCase {

    private static Logger LOGGER = Logger.getLogger(ComandosSelenium.class);

    private WebDriver driver;

    private Wait<WebDriver> esperaPadrao;

    private Wait<WebDriver> esperaRapida;

    /**
     * Construtor da classe.
     * 
     * @param driver {@link WebDriver}.
     */
    public ComandosSelenium(WebDriver driver) {

        super();
        this.driver = driver;
        this.esperaPadrao = new WebDriverWait(driver, 30);
        this.esperaRapida = new WebDriverWait(driver, 3);
    }

    /**
     * Para o {@link WebDriver}.
     */
    public void parar() {

        LOGGER.debug("Parando o WebDriver.");

        driver.quit();

    }

    /**
     * Abre a página informada
     * 
     * @param pagina Página.
     */
    public void abrir(String pagina) {

        LOGGER.debug(String.format("Abrindo página: (%s).", pagina));
        driver.navigate().to(pagina);
    }

    /**
     * Seleciona/marca um elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void marcar(String seletorCSS) {

        WebElement elemento = obterElementoPorSeletorCSS(seletorCSS);
        marcar(elemento);
    }

    /**
     * Seleciona/marca um elemento.
     * 
     * @param elemento Elemento.
     */
    public void marcar(WebElement elemento) {

        LOGGER.debug(String.format("Selecionando/marcando o elemento com o id: (%s).", elemento.getAttribute("id")));

        if (!elemento.isSelected()) {
            elemento.click();
            esperarElementoMarcado(elemento);
        }
    }

    /**
     * Remove a seleção/desmarca um elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void desmarcar(String seletorCSS) {

        WebElement element = obterElementoPorSeletorCSS(seletorCSS);
        desmarcar(element);
    }

    /**
     * Remove a seleção/desmarca um elemento.
     * 
     * @param elemento Elemento.
     */
    public void desmarcar(WebElement elemento) {

        LOGGER.debug(String.format("Removendo a seleção/desmarcando o elemento com o id: (%s).",
            elemento.getAttribute("id")));

        if (elemento.isSelected()) {
            elemento.click();
            esperarElementoNaoMarcado(elemento);
        }
    }

    /**
     * Verifica se o elemento possui o texto esperado.
     * 
     * @param elemento Elemento.
     * @param valor Texto esperado.
     * @return <tt>TRUE</tt> se tiver o texto esperado, <tt>FALSE</tt> caso contrário.
     */
    public boolean isElementoContemTexto(WebElement elemento, String valor) {

        LOGGER.debug(String.format("Verificando se o elemento com o id: (%s), possui o valor: %s.",
            elemento.getAttribute("id"), valor));

        return elemento.getText().contains(valor);
    }

    /**
     * Especifica o valor para o elemento.
     * 
     * @param elemento Elemento.
     * @param valor Valor.
     */
    public void definirValor(WebElement elemento, String valor) {

        LOGGER.debug(String.format("Preenchendo o elemento com o id: (%s) com o valor: %s.",
            elemento.getAttribute("id"), valor));

        esperarElementoVisivel(elemento);
        elemento.clear();
        elemento.sendKeys(valor);
    }

    /**
     * Limpa o elemento.
     * 
     * @param elemento Elemento.
     */
    public void limpaElemento(WebElement elemento) {

        LOGGER.debug(String.format("Limpando o conteúdo do elemento com o id: (%s).", elemento.getAttribute("id")));

        esperarElementoVisivel(elemento);
        elemento.clear();
    }

    /**
     * Clica no elemento.
     * 
     * @param elemento Elemento.
     */
    public void clicar(WebElement elemento) {

        LOGGER.debug(String.format("Clicando no elemento com o id: (%s).", elemento.getAttribute("id")));

        esperarElementoVisivel(elemento);
        elemento.click();
    }

    /**
     * Seleciona o valor do elemento que possui o texto informado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param texto Texto.
     */
    public void selecionarPorTextoVisivel(String seletorCSS, String texto) {

        LOGGER.debug(String.format("Selecionando o elemento com o seletor CSS: (%s) e que possui o texto: %s.",
            seletorCSS, texto));

        new Select(obterElementoPorSeletorCSS(seletorCSS)).selectByVisibleText(texto);
    }

    /**
     * Seleciona o valor do elemento que possui o valor informado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param valor Valor do elemento.
     */
    public void selecionarPorValor(String seletorCSS, String valor) {

        LOGGER.debug(String.format("Selecionando o elemento com o seletor CSS: (%s) e que possui o valor: %s.",
            seletorCSS, valor));

        new Select(obterElementoPorSeletorCSS(seletorCSS)).selectByValue(valor);
    }

    /**
     * Seleciona a opção <tt>OK</tt> da caixa de confirmação.
     */
    public void escolherOKEmProximaConfirmacao() {

        LOGGER.debug(String.format("Selecionando a opção OK na caixa de confirmação."));

        driver.switchTo().alert().accept();
    }

    /**
     * Abre a página em uma janela.
     * 
     * @param pagina Página.
     * @param nomeJanela Nome da janela.
     */
    public void abrirJanela(String pagina, String nomeJanela) {

        LOGGER.debug(String.format("Abrindo a página: (%s) na janela: %s.", pagina, nomeJanela));

        driver.switchTo().window(nomeJanela).navigate().to(pagina);
    }

    /**
     * Seleciona uma janela.
     * 
     * @param nomeJanela Nome da janela.
     */
    public void selecionarJanela(String nomeJanela) {

        LOGGER.debug(String.format("Selecionando a janela com nome: (%s).", nomeJanela));

        driver.switchTo().window(nomeJanela);
    }

    /**
     * Verifica se o elemento está presente.
     * 
     * @param elemento Elemento.
     * @return <tt>TRUE</tt> se tiver presente, <tt>FALSE</tt> caso contrário.
     */
    public boolean isElementoPresente(WebElement elemento) {

        LOGGER.debug(String.format("Verificando se o elemento com o id: (%s), está presente.",
            elemento.getAttribute("id")));

        try {
            return elemento != null;
        } catch (NoSuchElementException e) {
            return false;
        }
    }

    /**
     * Verifica se o elemento está visível.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @return <tt>TRUE</tt> se tiver visível, <tt>FALSE</tt> caso contrário.
     */
    public boolean isVisivel(String seletorCSS) {

        WebElement elemento = obterElementoPorSeletorCSS(seletorCSS);

        return isVisivel(elemento);
    }

    /**
     * Verifica se o elemento está visível.
     * 
     * @param elemento Elemento.
     * @return <tt>TRUE</tt> se tiver visível, <tt>FALSE</tt> caso contrário.
     */
    public boolean isVisivel(WebElement elemento) {

        LOGGER.debug(String.format("Verificando se o elemento com o id: (%s), está visível.",
            elemento.getAttribute("id")));

        try {
            return elemento != null ? elemento.isDisplayed() : false;
        } catch (ElementNotVisibleException e) {
            return false;
        } catch (NoSuchElementException e) {
            return false;
        }
    }

    /**
     * Retorna o texto do elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @return Texto do elemento.
     */
    public String obterTexto(String seletorCSS) {

        return obterTexto(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Retorna o texto do elemento.
     * 
     * @param elemento Elemento.
     * @return Texto do elemento.
     */
    public String obterTexto(WebElement elemento) {

        LOGGER.debug(String.format("Obtendo o texto do elemento com o id: (%s).", elemento.getAttribute("id")));

        esperarElementoVisivel(elemento);
        return elemento.getText();
    }

    /**
     * Retorna o valor do elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @return Valor do elemento.
     */
    public String obterValor(String seletorCSS) {

        return obterValor(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Retorna o valor do elemento.
     * 
     * @param elemento {@link WebElement} Elemento.
     * @return Valor do elemento.
     */
    public String obterValor(WebElement elemento) {

        LOGGER.debug(String.format("Obtendo o valor do elemento com o id: (%s).", elemento.getAttribute("id")));

        return elemento.getAttribute("value");
    }

    /**
     * Retorna o atributo do elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param nomeAtributo Nome do atributo.
     * @return Retorna o atributo.
     */
    public String obterAtributo(String seletorCSS, String nomeAtributo) {

        return obterAtributo(obterElementoPorSeletorCSS(seletorCSS), nomeAtributo);
    }

    /**
     * Retorna o atributo do elemento.
     * 
     * @param elemento Elemento.
     * @param nomeAtributo Nome do atributo.
     * @return Retorna o atributo.
     */
    public String obterAtributo(WebElement elemento, String nomeAtributo) {

        LOGGER.debug(String.format("Obtendo do elemento com o id: (%s), o atributo com nome: %s.",
            elemento.getAttribute("id"), nomeAtributo));

        return elemento.getAttribute(nomeAtributo);
    }

    /**
     * Retorna o valor do atributo.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @return Valor do atributo.
     */
    public String obterValorAtributo(String seletorCSS) {

        String[] locatorAndAttrName = seletorCSS.split("@");
        if (locatorAndAttrName.length != 2) {
            throw new IllegalArgumentException("Sintaxe esperada: cssLocator@nomeAtributo");
        }
        return obterAtributo(locatorAndAttrName[0], locatorAndAttrName[1]);
    }

    /**
     * Retorna a quantidade de elementos que possuem o seletor CSS.
     * 
     * @param seletorCSS Seletor CSS.
     * @return Quantidade de elementos.
     */
    public int obterQuantidadeElementosPorSeletorCSS(String seletorCSS) {

        return obterElementosPorSeletorCSS(seletorCSS).size();
    }

    /**
     * Retorna a quantidade de elementos que possuem a classe CSS.
     * 
     * @param classeCSS Seletor CSS.
     * @return Quantidade de elementos.
     */
    public int obterQuantidadeElementosPorClasseCSS(String classeCSS) {

        return obterElementosPorClasseCSS(classeCSS).size();
    }

    /**
     * Retorna o ID da sessão.
     * 
     * @return ID da sessão.
     */
    public String obterSessionId() {

        LOGGER.debug("Obtendo o ID da Sessão.");

        Cookie jSessionId = driver.manage().getCookieNamed("JSESSIONID");
        return jSessionId != null ? jSessionId.getValue() : null;
    }

    /**
     * Verifica se o título da página é igual ao informado.
     * 
     * @param titulo Título esperado.
     */
    public void assertTitle(String titulo) {

        assertEquals("O titulo da pagina nao era o esperado", titulo, driver.getTitle());
    }

    /**
     * Verifica se o texto do elemento é igual ao informado.
     * 
     * @param seletorCSS Seletor CSS do elemento que será verificado.
     * @param valor Valor esperado.
     */
    public void assertTextoIgual(String seletorCSS, String valor) {

        assertTextoIgual(obterElementoPorSeletorCSS(seletorCSS), valor);
    }

    /**
     * Verifica se o texto do elemento é igual ao informado.
     * 
     * @param elemento Elemento que será verificado.
     * @param valor Valor esperado.
     */
    public void assertTextoIgual(WebElement elemento, String valor) {

        String text = elemento.getText();
        assertTrue("O texto do elemento nao era o esperado", text.contains(valor));
    }

    /**
     * Verifica se o elemento está presente.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoPresente(String seletorCSS) {

        assertElementoPresente(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Verifica se o elemento está presente.
     * 
     * @param elemento Elemento.
     */
    public void assertElementoPresente(WebElement elemento) {

        try {
            assertNotNull("Elemento esperado nao foi encontrado", elemento);
        } catch (NoSuchElementException e) {
            fail("Elemento esperado nao foi encontrado");
        }
    }

    /**
     * Verifica se o elemento está ausente.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoNaoPresente(String seletorCSS) {

        assertElementoNaoPresente(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Verifica se o elemento está ausente.
     * 
     * @param elemento Elemento.
     */
    public void assertElementoNaoPresente(WebElement elemento) {

        try {
            assertNull("Elemento nao deveria existir", elemento);
        } catch (NoSuchElementException e) {
            // Esta' ok se lancar esta excecao...
        }
    }

    /**
     * Verifica se o elemento está visível.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoVisivel(String seletorCSS) {

        assertElementoVisivel(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Verifica se o elemento está visível.
     * 
     * @param elemento Elemento.
     */
    public void assertElementoVisivel(WebElement elemento) {

        try {
            assertTrue("Elemento que deveria estar visivel esta invisivel", elemento.isDisplayed());
        } catch (NoSuchElementException e) {
            fail("Elemento que deveria estar visivel nao existe no codigo da tela");
        } catch (ElementNotVisibleException e) {
            fail("Elemento que deveria estar visivel esta invisivel");
        }
    }

    /**
     * Verifica se o elemento está invisível.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoNaoVisivel(String seletorCSS) {

        try {

            assertElementoNaoVisivel(obterElementoPorSeletorCSS(seletorCSS));
        } catch (NoSuchElementException e) {
            fail("Elemento que deveria estar invisivel nao existe no codigo da tela");
        }
    }

    /**
     * Verifica se o elemento está invisível.
     * 
     * @param elemento Elemento.
     */
    public void assertElementoNaoVisivel(WebElement elemento) {

        try {

            assertFalse("Elemento que deveria estar invisivel esta visivel", elemento.isDisplayed());
        } catch (NoSuchElementException e) {
            fail("Elemento que deveria estar invisivel nao existe no codigo da tela");
        } catch (ElementNotVisibleException e) {
            assertTrue("Está ok se lançar esta exceção.", (elemento != null && !elemento.isDisplayed()));
        }
    }

    /**
     * Verifica se o texto está presente.
     * 
     * @param texto Texto.
     */
    public void assertTextoPresente(String texto) {

        assertTrue("O texto deveria ter sido encontrado na pagina", driver.getPageSource().contains(texto));
    }

    /**
     * Verifica se o texto não está presente.
     * 
     * @param texto Texto.
     */
    public void assertTextoNaoPresente(String texto) {

        assertFalse("O texto nao deveria ter sido encontrado na pagina", driver.getPageSource().contains(texto));
    }

    /**
     * Verifica se o elemento possui o valor informado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param valor Valor esperado.
     */
    public void assertValorIgual(String seletorCSS, String valor) {

        assertValorIgual(obterElementoPorSeletorCSS(seletorCSS), valor);
    }

    /**
     * Verifica se o elemento possui o valor informado.
     * 
     * @param elemento Elemento.
     * @param valor Valor esperado.
     */
    public void assertValorIgual(WebElement elemento, String valor) {

        assertEquals("O valor do elemento nao e' o esperado", valor, elemento.getAttribute("value"));
    }

    /**
     * Verifica se o elemento selecionado é o esperado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param valor Valor esperado.
     */
    public void assertElementoSelecionadoValorIgual(String seletorCSS, String valor) {

        assertElementoSelecionadoValorIgual(obterElementoPorSeletorCSS(seletorCSS), valor);
    }

    /**
     * Verifica se o elemento selecionado é o esperado.
     * 
     * @param elemento Elemento.
     * @param valor Valor esperado.
     */
    public void assertElementoSelecionadoValorIgual(WebElement elemento, String valor) {

        Select select = new Select(elemento);
        assertEquals("O elemento selecionado nao e' o esperado", valor, select.getFirstSelectedOption().getText());
    }

    /**
     * Verifica se o elemento está selecionado/marcado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoMarcado(String seletorCSS) {

        assertElementoMarcado(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Verifica se o elemento está selecionado/marcado.
     * 
     * @param elemento Elemento.
     */
    public void assertElementoMarcado(WebElement elemento) {

        assertTrue("O elemento nao esta selecionado", elemento.isSelected());
    }

    /**
     * Verifica se o elemento não está selecionado/desmarcado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoNaoMarcado(String seletorCSS) {

        assertElementoNaoMarcado(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Verifica se o elemento não está selecionado/desmarcado.
     * 
     * @param elemento Elemento.
     */
    public void assertElementoNaoMarcado(WebElement elemento) {

        assertFalse("O elemento esta selecionado", elemento.isSelected());
    }

    /**
     * Verifica se o valor do atributo é o esperado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param nomeAtributo Nome do atributo.
     * @param valor Valor esperado.
     */
    public void assertAtributoValorIgual(String seletorCSS, String nomeAtributo, String valor) {

        assertEquals("Valor do atributo nao era o esperado", valor, obterAtributo(seletorCSS, nomeAtributo));
    }

    /**
     * Verifica se o elemento é o esperado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void assertElementoEsperado(String seletorCSS) {

        assertEquals("O elemento nao e' o esperado", seletorCSS, driver.getCurrentUrl());
    }

    /**
     * Verifica se a quantidade de elementos é a esperada.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param qtdEsperada Quantidade esperada.
     */
    public void assertQuantidadeElementosPorSeletorCSS(String seletorCSS, int qtdEsperada) {

        assertEquals("Numero inesperado de elementos", qtdEsperada, obterElementosPorSeletorCSS(seletorCSS).size());
    }

    /**
     * Verifica se a quantidade de elementos é a esperada.
     * 
     * @param classeCSS Classe CSS do elemento.
     * @param qtdEsperada Quantidade esperada.
     */
    public void assertQuantidadeElementosPorClasseCSS(String classeCSS, int qtdEsperada) {

        assertEquals("Numero inesperado de elementos", qtdEsperada, obterElementosPorClasseCSS(classeCSS).size());
    }

    /**
     * Verifica se o valor da célula é o esperado.
     * 
     * @param idTabela ID da tabela.
     * @param numeroLinha Número da linha.
     * @param numeroColuna Número da coluna.
     * @param texto Valor esperado.
     */
    public void assertCelulaTabelaTextoIgual(String idTabela, int numeroLinha, int numeroColuna, String texto) {

        assertEquals("O valor da celula da tabela " + idTabela + " nao corresponde ao esperado", texto,
            obterElementoPorSeletorCSS(obterSeletorCSSCelulaEmTabela(idTabela, numeroLinha, numeroColuna)).getText());
    }

    /**
     * Verifica se o valor da célula é diferente do esperado.
     * 
     * @param idTabela ID da tabela.
     * @param numeroLinha Número da linha.
     * @param numeroColuna Número da coluna.
     * @param texto Valor esperado.
     */
    public void assertCelulaTabelaTextoNaoIgual(String idTabela, int numeroLinha, int numeroColuna, String texto) {

        assertFalse("O valor da celula da tabela " + idTabela + " e' o mesmo que o informado",
            texto.equals(obterElementoPorSeletorCSS(obterSeletorCSSCelulaEmTabela(idTabela, numeroLinha, numeroColuna))
                .getText()));
    }

    /**
     * Verifica se alguma linha da tabela possui todos os valores informados.
     * 
     * @param idTabela ID da tabela.
     * @param valoresNaLinha Valores na linha.
     */
    public void assertValoresExistenteEmLinhaTabela(String idTabela, String... valoresNaLinha) {

        List<WebElement> linhasTabela = driver.findElements(By.cssSelector(idTabela + " tr"));
        int numMatchedValues = -1;

        for (WebElement linhaTabela : linhasTabela) {
            numMatchedValues = 0;
            String rowText = linhaTabela.getText();

            for (String rowCellValue : valoresNaLinha) {
                if (rowText.contains(rowCellValue))
                    numMatchedValues++;
            }

            if (numMatchedValues == valoresNaLinha.length)
                break;
        }

        assertNotSame("A tabela informada nao possui linhas", -1, numMatchedValues);

        assertSame("Nenhuma linha da tabela possui todos os valores especificados", valoresNaLinha.length,
            numMatchedValues);
    }

    /*
     * Metodos para espera
     */

    /**
     * Espera por um título.
     * 
     * @param valor Valor do título.
     */
    public void esperarPorTitulo(final String valor) {

        LOGGER.debug(String.format("Esperando que o título da página possua o valor: (%s).", valor));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return driver.getTitle().contains(valor);
            }
        });
    }

    /**
     * Espera por uma confirmação.
     * 
     * @param valor Valor da confirmação.
     */
    @Deprecated
    public void esperarPorConfirmacao(final String valor) {

        esperarPorAlerta(valor);
    }

    /**
     * Espero pela presença de um elemento.
     * 
     * @param elemento Elemento.
     */
    public void esperarPorPresencaElemento(final WebElement elemento) {

        LOGGER
            .debug(String.format("Esperando que o elemento o id: (%s), esteja presente.", elemento.getAttribute("id")));

        esperaPadrao.until(new ExpectedCondition<WebElement>() {

            public WebElement apply(WebDriver driver) {

                return elemento;
            }
        });
    }

    /**
     * Espero pela ausencia de um elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void esperarPorAusenciaElemento(final String seletorCSS) {

        LOGGER.debug(String
            .format("Esperando que o elemento com o seletor CSS: (%s), não esteja presente.", seletorCSS));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                try {
                    return obterElementoPorSeletorCSS(seletorCSS) == null;
                } catch (NotFoundException e) {
                    return true;
                }
            }
        });
    }

    /**
     * Espera que o elemento esteja visível.
     * 
     * @param elemento Elemento.
     */
    public void esperarElementoVisivel(final WebElement elemento) {

        LOGGER
            .debug(String.format("Esperando que o elemento o id: (%s), esteja visível.", elemento.getAttribute("id")));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return elemento != null ? elemento.isDisplayed() : false;
            }
        });
    }

    /**
     * Espera que o elemento esteja invisível.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void esperarElementoInvisivel(final String seletorCSS) {

        esperarElementoInvisivel(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Espera que o elemento esteja invisível.
     * 
     * @param elemento Elemento.
     */
    public void esperarElementoInvisivel(final WebElement elemento) {

        LOGGER.debug(String.format("Esperando que o elemento o id: (%s), não esteja visível.",
            elemento.getAttribute("id")));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                try {
                    return elemento != null ? !elemento.isDisplayed() : true;
                } catch (ElementNotVisibleException e) {
                    return true;
                } catch (NoSuchElementException e) {
                    return true;
                }
            }
        });
    }

    /**
     * Espera que o elemento esteja selecionado/marcado.
     * 
     * @param elemento Elemento.
     */
    public void esperarElementoMarcado(final WebElement elemento) {

        LOGGER.debug(String.format("Esperando que o elemento o id: (%s), esteja selecionado/marcado",
            elemento.getAttribute("id")));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return elemento.isSelected();
            }
        });
    }

    /**
     * Espera que o elemento não esteja selecionado/marcado.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void esperarElementoNaoMarcado(final String seletorCSS) {

        esperarElementoNaoMarcado(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Espera que o elemento não esteja selecionado/marcado.
     * 
     * @param elemento Elemento.
     */
    public void esperarElementoNaoMarcado(final WebElement elemento) {

        LOGGER.debug(String.format("Esperando que o elemento o id: (%s), não esteja selecionado/marcado",
            elemento.getAttribute("id")));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return !elemento.isSelected();
            }
        });
    }

    /**
     * Espera que o texto esteja presente na página.
     * 
     * @param texto Texto.
     */
    public void esperarTextoPresente(final String texto) {

        LOGGER.debug(String.format("Esperando que na página possua o texto: %s.", texto));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return driver.getPageSource().contains(texto);
            }
        });
    }

    /**
     * Espera que o texto esteja presente no elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param texto Texto.
     */
    public void esperarTextoPresente(final String seletorCSS, final String texto) {

        esperarTextoPresente(obterElementoPorSeletorCSS(seletorCSS), texto);
    }

    /**
     * Espera que o texto esteja presente no elemento.
     * 
     * @param elemento Elemento.
     * @param texto Texto.
     */
    public void esperarTextoPresente(final WebElement elemento, final String texto) {

        LOGGER.debug(String.format("Esperando que o elemento o id: (%s), possua o texto: %s.",
            elemento.getAttribute("id"), texto));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return texto.equals(elemento.getText());
            }
        });
    }

    /**
     * Espera por um determinado padrão. <p>Tudo que for pattern talvez receba * nos PageObjects, e talvez isso nao
     * funcione</p>.
     * 
     * @param padrao Padrão.
     */
    public void esperarPorAlerta(final String padrao) {

        LOGGER.debug(String.format("Esperando por um alerta que possua no texto o padrão: (%s).", padrao));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return driver.switchTo().alert().getText().contains(padrao);
            }
        });
    }

    /**
     * Espera por uma determinada quantidade de elementos.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param qtdEsperada Quantidade esperada.
     */
    public void esperarPorQuantidadeElementosPorSeletorCSS(final String seletorCSS, final int qtdEsperada) {

        LOGGER.debug(String.format("Esperando que existam %s elementos com o seletor CSS: (%s).", qtdEsperada,
            seletorCSS));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return obterElementosPorSeletorCSS(seletorCSS).size() == qtdEsperada;
            }
        });
    }

    /**
     * Espera por uma determinada quantidade de elementos.
     * 
     * @param classeCSS Classe CSS do elemento.
     * @param qtdEsperada Quantidade esperada.
     */
    public void esperarPorQuantidadeElementosPorClasseCSS(final String classeCSS, final int qtdEsperada) {

        LOGGER.debug(String
            .format("Esperando que existam %s elementos com a classe CSS: (%s).", qtdEsperada, classeCSS));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return obterElementosPorClasseCSS(classeCSS).size() == qtdEsperada;
            }
        });
    }

    /**
     * Espera que o valor possua um determinado padrão.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     * @param padrao Padrão.
     */
    public void esperarPorValorComPadrao(final String seletorCSS, final String padrao) {

        esperarPorValorComPadrao(obterElementoPorSeletorCSS(seletorCSS), padrao);
    }

    /**
     * Espera que o valor possua um determinado padrão.
     * 
     * @param elemento Elemento.
     * @param padrao Padrão.
     */
    public void esperarPorValorComPadrao(final WebElement elemento, final String padrao) {

        LOGGER.debug(String.format("Esperando que o elemento com o id: (%s), esteja visível.",
            elemento.getAttribute("id")));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return obterValor(elemento).contains(padrao);
            }
        });
    }

    /**
     * Espera que o elemento carregado por Ajax esteja visível.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void esperarPorAjax(String seletorCSS) {

        esperarPorAjax(obterElementoPorSeletorCSS(seletorCSS));
    }

    /**
     * Espera que o elemento carregado por Ajax esteja visível.
     * 
     * @param elemento Elemento.
     */
    public void esperarPorAjax(WebElement elemento) {

        LOGGER.debug(String.format("Esperando que o elemento com o id: (%s), esteja visível.",
            elemento.getAttribute("id")));

        for (int i = 0; i < 10; i++) {

            if (elemento.isDisplayed()) {
                return;
            }

            try {

                Thread.sleep(1000);
            } catch (Exception e) {
                LOGGER.error("Erro ao gerar espera por 1 segundo.", e);
            }
        }
    }

    /**
     * Espera por um popup.
     * 
     * @param nomeJanela Nome da janela popup.
     */
    public void esperarPorPopUp(final String nomeJanela) {

        esperaRapida.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                driver.switchTo().window(nomeJanela);
                // se nao encontrar lançara NoSuchWindowException
                return true;
            }
        });
    }

    /**
     * Espera por qualquer valor no elemento.
     * 
     * @param seletorCSS Seletor CSS do elemento.
     */
    public void esperarPorQualquerValor(final String seletorCSS) {

        LOGGER.debug(String.format("Esperando que o elemento com o seletor CSS: (%s), tenha qualquer valor.",
            seletorCSS));

        esperaPadrao.until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driver) {

                return obterValor(obterElementoPorSeletorCSS(seletorCSS)).matches(".+");
            }
        });
    }

    /**
     * Espera pelos segundos especificados.
     * 
     * @param segundos Quantidade de segundos.
     */
    public void esperarPorSegundos(long segundos) {

        try {
            Thread.sleep(segundos * 1000);
        } catch (InterruptedException e) {
            LOGGER.error("Erro ao gerar espera por segundos.", e);
        }
    }

    /**
     * Retorna o elemento baseado no seletor CSS.
     * 
     * @param seletorCSS Seletor CSS.
     * @return {@link WebElement}.
     */
    private WebElement obterElementoPorSeletorCSS(String seletorCSS) {

        LOGGER.debug(String.format("Obtendo elemento com o seletor CSS: (%s).", seletorCSS));

        return driver.findElement(By.cssSelector(seletorCSS));
    }

    /**
     * Retorna os elementos baseado no seletor CSS.
     * 
     * @param seletorCSS Seletor CSS.
     * @return Lista de {@link WebElement}.
     */
    private List<WebElement> obterElementosPorSeletorCSS(String seletorCSS) {

        LOGGER.debug(String.format("Obtendo elementos com o seletor CSS: (%s).", seletorCSS));

        return driver.findElements(By.cssSelector(seletorCSS));
    }

    /**
     * Retorna os elementos baseado na classe CSS.
     * 
     * @param classeCSS Classe CSS.
     * @return Lista de {@link WebElement}.
     */
    private List<WebElement> obterElementosPorClasseCSS(String classeCSS) {

        LOGGER.debug(String.format("Obtendo elementos com a classe CSS: (%s).", classeCSS));

        return driver.findElements(By.className(classeCSS));
    }

    /**
     * Retorna o seletor CSS da célula especificada pela linha e coluna da tabela informada.
     * 
     * @param idTabela ID da tabela.
     * @param numeroLinha Número da linha.
     * @param numeroColuna Número da coluna.
     * @return Seletor CSS da célula.
     */
    private String obterSeletorCSSCelulaEmTabela(String idTabela, int numeroLinha, int numeroColuna) {

        StringBuilder locator = new StringBuilder() //
            .append("table#" + idTabela) //
            .append(" ") //
            .append("tr:nth(").append(numeroLinha - 1).append(")") //
            .append(" > ") //
            .append("td:nth(").append(numeroColuna - 1).append(")");

        return locator.toString();
    }

}
